import Strings from '@/i18n';
import { publishDps } from '@ray-js/ray';
import dayjs from 'dayjs';
import { times, map, isUndefined, forEach, get, endsWith } from 'lodash-es';

type TimerKey = 'normalTiming' | 'cycleTiming';

const AreaArr = ['0001', '0002', '0004', '0008', '0010', '0020', '0040', '0080', '0100', '0200'];

export interface NormalTimerData {
  id: string;
  startTime: number;
  waterTime: number;
  endTime: number;
  loops: (string | number)[];
  power: boolean;
  waterArea: number | string;
  type?: string;
  repeatStr?: string;
  areaStr?: string;
}

export interface CycleTimerData {
  id: string;
  startTime: number;
  endTime: number;
  pauseTime: number;
  waterTime: number;
  loops: (string | number)[];
  waterArea: number | string;
  power: boolean;
  type?: string;
  repeatStr?: string;
  areaStr?: string;
}

const getBitValue = (num, idx) => (num & (1 << idx)) >> idx;

const toFixed = (str, count) => {
  return `${'0'.repeat(count)}${str}`.slice(-1 * count);
};

export const setLoopMode = (value: number) => {
  const mode: any = {
    0: '00',
    1: '01',
    2: '02',
    3: '03',
  };
  return mode[value];
};

// 十六进制Hex转base64
export const hexToBase64 = (str = '') => {
  return btoa(
    String.fromCharCode.apply(
      null,
      str
        .replace(/\r|\n/g, '')
        .replace(/([\da-fA-F]{2}) ?/g, '0x$1 ')
        .replace(/ +$/, '')
        .split(' ')
    )
  );
};

const numToHexString = (num, padding = 2) => {
  const hex = Number(num).toString(16);
  return toFixed(hex, padding);
};

export const toLoopBinString = loop => {
  console.log(`🚀 loop`, loop);
  const str = times(7, n => {
    const ret = getBitValue(loop, n);
    return `${ret}`;
  }).join('');
  return str;
};

export const getLoopMode = (value: string) => {
  const mode: any = {
    '00': 0,
    '01': 1,
    '02': 2,
    '03': 3,
  };
  return mode[value];
};

export function convertToHoursMinutes(totalMinutes: number): string {
  const hours = Math.floor(totalMinutes / 60);
  const minutes = totalMinutes % 60;

  const hourText = hours === 0 ? '' : `${hours}`.padStart(2, '0') + Strings.getLang('hour');

  const minsText = `${minutes}`.padStart(2, '0') + Strings.getLang('min');

  // return { hours, minutes };

  return hourText + minsText;
}

export const getTimeArr = (timeArr: any[]) => {
  return (
    (parseInt(timeArr[0]) !== 0 ? `${timeArr[0]}${Strings.getLang('hour')}` : '') +
    (parseInt(timeArr[1]) !== 0 ? `${timeArr[1]}${Strings.getLang('min')}` : '')
  );
};
/**
 * 根据二进制循环字符串输出多语言包
 * @param {String} source - something like this '0000000'
 */
export const getRepeatStr = source => {
  if (!source) return '';
  const days = [];
  let repeat = '';
  source.split('').forEach((item, index) => {
    if (item === '1') {
      days.push(Strings.getLang(`day_${index}`));
    }
  });
  if (days.length === 0) {
    repeat = Strings.getLang('dayOnce');
  } else if (days.length === 7) {
    repeat = Strings.getLang('dayEvery');
  } else if (days.length === 5 && source.substring(1, 6) === '11111') {
    repeat = Strings.getLang('weekDays');
  } else if (days.length === 2 && source.startsWith('1') && source.endsWith('1')) {
    repeat = Strings.getLang('weekend');
  } else {
    repeat = days.join('、');
  }
  return repeat;
};

const areaSet = ['01', '02', '04', '08', '10', '20', '40', '80'];
/**
 * second -> [minute, second]
 * @param {Number} t - 秒
 */
export const parseMinutes = (t: number) => {
  const m = parseInt(t / 60, 10);
  const s = parseInt(t % 60, 10);
  return [m, s];
};

function isTimeConflict(time1, time2) {
  return Math.max(time1.startTime, time2.startTime) < Math.min(time1.endTime, time2.endTime);
}

function getCurrentMinutes() {
  const now = new Date();
  return now.getHours() * 60 + now.getMinutes();
}

function isDayConflict(day1, day2) {
  for (const time1 of day1) {
    for (const time2 of day2) {
      if (isTimeConflict(time1, time2)) {
        return true;
      }
    }
  }
  return false;
}

function hasScheduleConflict(week1, week2) {
  for (let i = 0; i < 7; i++) {
    if (isDayConflict(week1[i] || [], week2[i] || [])) {
      return true;
    }
  }
  return false;
}

function weekOverlap(
  newTimer: NormalTimerData | CycleTimerData,
  exitTime: NormalTimerData | CycleTimerData
) {
  const loops = Array.isArray(newTimer.loops) ? newTimer.loops : toLoopBinString(newTimer.loops);
  console.log(`🚀 weekOverlap ~ loops:`, loops);

  const exitLoops = Array.isArray(exitTime.loops)
    ? exitTime.loops
    : toLoopBinString(exitTime.loops);
  console.log(`🚀 weekOverlap ~ exitLoops:`, exitLoops);

  const now = getCurrentMinutes();
  const today = new Date();
  const dayOfWeek = today.getDay(); // 返回0(周日)到6(周六)
  const realLoop = [...loops];

  if (newTimer.loops.every(item => item == 0)) {
    if (now < newTimer.startTime) {
      realLoop[dayOfWeek] = '1';
    } else if (now >= newTimer.startTime) {
      realLoop[dayOfWeek + 1 > 6 ? 0 : dayOfWeek + 1] = '1';
    }
  }
  const realExitLoop = [...exitLoops];

  if (exitTime.loops.every(item => item == 0)) {
    if (now < newTimer.endTime) {
      realExitLoop[dayOfWeek] = '1';
    } else if (now >= newTimer.endTime) {
      realExitLoop[dayOfWeek + 1 > 6 ? 0 : dayOfWeek + 1] = '1';
    }
  }

  const wholeLoops = [...loops, ...realLoop, ...loops];

  let wholeExitLoops = [...exitLoops, ...realExitLoop, ...exitLoops];

  const isExitNext = exitTime.startTime > exitTime.endTime;

  const isCurrentTimeNext = newTimer.startTime > newTimer.endTime;

  let wholeExitTimer = [];

  let wholeNewTimer = [];

  for (let i = 7; i < 14; i++) {
    const prev = wholeExitLoops[i - 1];
    const item = wholeExitLoops[i];
    let current = [];
    if (isExitNext) {
      if (prev == 1) {
        current.push({ startTime: 0, endTime: exitTime.endTime });
      }
      if (item == 1) {
        current.push({ startTime: exitTime.startTime, endTime: 1440 });
      }
      wholeExitTimer.push(current);
    } else {
      if (item == 1) {
        current.push({ startTime: exitTime.startTime, endTime: exitTime.endTime });
      }
      wholeExitTimer.push(current);
    }
  }

  for (let i = loops.length; i < loops.length * 2; i++) {
    const prev = wholeLoops[i - 1];
    const item = wholeLoops[i];

    let current = [];
    if (isCurrentTimeNext) {
      if (prev == 1) {
        current.push({ startTime: 0, endTime: newTimer.endTime });
      }
      if (item == 1) {
        current.push({ startTime: newTimer.startTime, endTime: 1440 });
      }
      wholeNewTimer.push(current);
    } else {
      if (item == 1) {
        current.push({ startTime: newTimer.startTime, endTime: newTimer.endTime });
      }
      wholeNewTimer.push(current);
    }
  }

  const res = hasScheduleConflict(wholeExitTimer, wholeNewTimer); // true 代表有 fasle 代表不冲突

  return res;
}

// 时间段重叠（支持跨天，start/end为分钟数）
export function timeOverlap(start1, end1, start2, end2) {
  // 不跨天
  if (start1 < end1 && start2 < end2) {
    return Math.max(start1, start2) < Math.min(end1, end2);
  }
  // start1跨天
  if (start1 > end1) {
    return timeOverlap(start1, 1440, start2, end2) || timeOverlap(0, end1, start2, end2);
  }
  // start2跨天
  if (start2 > end2) {
    return timeOverlap(start1, end1, start2, 1440) || timeOverlap(start1, end1, 0, end2);
  }
  return false;
}

const conflictWithExistTimer = (
  existTimer: NormalTimerData | CycleTimerData,
  data: NormalTimerData | CycleTimerData
) => {
  const {
    startTime: existStartTime,
    waterTime: existWaterTime,
    waterArea: existWaterArea,
    endTime: existEndTime,
    loops: existLoops,
  } = existTimer;

  const {
    startTime: newStartTime,
    waterTime: newWaterTime,
    waterArea: newWaterArea,
    endTime: newEndTime,
    loops: newLoops,
  } = data;

  if (newWaterArea !== existWaterArea) return false;
  console.log(`🚀 ~ step1:`);

  if (!timeOverlap(existStartTime, existEndTime, newStartTime, newEndTime)) {
    return false;
  }
  console.log(`🚀 ~ step2:`);

  const week = weekOverlap(data, existTimer);

  console.log(`🚀 ~ week:`, week);

  if (!weekOverlap(data, existTimer)) {
    return false;
  }

  console.log(`🚀 ~ step3:`);

  return true;
};

export const checkTimerConflict = ({
  data,
  normalDataSource,
  cycleDataSource,
}: {
  data: NormalTimerData | CycleTimerData;
  normalDataSource: NormalTimerData[];
  cycleDataSource: CycleTimerData[];
}) => {
  console.log(`🚀 ~ cycleDataSource:`, cycleDataSource, normalDataSource);
  if (!data.power) {
    return {
      result: true,
      conflictTip: '',
    };
  }
  const result = [];

  for (const existTimer of [...normalDataSource, ...cycleDataSource]) {
    if (!existTimer.power) continue;
    if (conflictWithExistTimer(existTimer as NormalTimerData | CycleTimerData, data)) {
      result.push(existTimer);
    }
  }

  console.log('🚀 ~ checkTimerConflict ~ result:', result);
  if (result.length) {
    return {
      result: false,
      conflictTip: '与已存在的定时任务冲突',
    };
  }

  return {
    result: true,
    conflictTip: '',
  };
};

export const decodeNormalTimer = (vals: string, isRaw = true, dpsInfo: any): NormalTimerData[] => {
  if (!vals) return [];
  // const value = isRaw ? vals.slice(2) : base64ToHex(vals);
  // 1303e6000100060001000100010001000100010000300107e90615
  const value = vals;
  const len = 16;
  const isNotLeng = value.length % len !== 0 || (value !== '' && value.length < len);
  if (!value || isNotLeng) return [];
  const valueStr: any = value.match(/[0-9|a-f|A-F]{16}/g);
  const strArr: any = [];
  valueStr.map((val: string) => {
    if (val.length === len) {
      const idPowerStr = parseInt(val.slice(0, 2), 16).toString(2).padStart(8, '0');

      const power = idPowerStr[idPowerStr.length - 1] === '1';

      const orderNo = parseInt(idPowerStr.slice(0, idPowerStr.length - 1), 2);

      const startTime = parseInt(val.slice(2, 6), 16);

      const areaStr = val.slice(6, 10);

      const waterTime = parseInt(val.slice(10, 14), 16);

      const loopStr = val.slice(14, 16);

      const loopsStr2 = parseInt(loopStr === '80' ? '00' : loopStr, 16);

      const loops = toLoopBinString(loopsStr2);

      const normalData = {
        type: 'normalTime',
        id: orderNo,
        startTime,
        waterTime,
        loops: loops.split(''),
        power,
        waterArea: AreaArr.findIndex(item => item === areaStr),
        repeatStr: getRepeatStr(toLoopBinString(loopsStr2)),
        endTime: startTime + waterTime,
        areaStr,
      };
      strArr.push(normalData);
    }
  });

  // const strWithNo = strArr.filter(item => item.no == selected);
  return strArr;
};

export const decodeCycleTimer = (vals: string, isRaw = true, dpsInfo: any): CycleTimerData[] => {
  if (!vals) return [];

  const value = vals;
  const len = 24;
  const isNotLeng = value.length % len !== 0 || (value !== '' && value.length < len);
  if (!value || isNotLeng) return [];
  const valueStr: any = value.match(/[0-9|a-f|A-F]{24}/g);
  const strArr: any = [];
  valueStr.map((val: string) => {
    if (val.length === len) {
      const idPowerStr = parseInt(val.slice(0, 2), 16).toString(2).padStart(8, '0');

      const power = idPowerStr[idPowerStr.length - 1] === '1';

      const orderNo = parseInt(idPowerStr.slice(0, idPowerStr.length - 1), 2);

      const startTime = parseInt(val.slice(2, 6), 16);

      const endTime = parseInt(val.slice(6, 10), 16);

      const waterTime = parseInt(val.slice(10, 14), 16);

      const pauseTime = parseInt(val.slice(14, 18), 16);

      const areaStr = val.slice(18, 22);

      const loopStr = val.slice(22, 24);

      const loopsStr2 = parseInt(loopStr === '80' ? '00' : loopStr, 16);

      const loops = toLoopBinString(loopsStr2);

      const cycleData = {
        type: 'cycleTime',
        id: orderNo,
        startTime,
        endTime,
        waterTime,
        pauseTime,
        loops: loops.split(''),
        power,
        waterArea: AreaArr.findIndex(item => item === areaStr),
        repeatStr: getRepeatStr(toLoopBinString(loopsStr2)),
        areaStr,
      };
      strArr.push(cycleData);
    }
  });

  // const strWithNo = strArr.filter(item => item.no == selected);
  return strArr;
};

export const putTimerData = ({
  key,
  data,
  isEdit,
  normalDataSource,
  cycleDataSource,
  callback,
}: {
  key: TimerKey;
  data: NormalTimerData | CycleTimerData;
  isEdit: boolean;
  normalDataSource: NormalTimerData[];
  cycleDataSource: CycleTimerData[];
  callback: (isReject: boolean, conflictTip: string) => void;
}) => {
  const res = checkTimerConflict({
    data,
    normalDataSource: normalDataSource.filter(item => item.id != data.id || key === 'cycleTiming'),
    cycleDataSource: cycleDataSource.filter(item => item.id != data.id || key === 'normalTiming'),
  });

  if (res.result) {
    if (key === 'normalTiming') {
      if (isEdit) {
        const newList = normalDataSource.map(item => {
          if (item.id == data.id) {
            return data;
          }
          return item;
        });

        console.log(`🚀 ~ newList:`, newList);

        publishDps({
          general_timing: encodeAllNormalTimer(newList as NormalTimerData[]),
        });
      } else {
        const newList = [...normalDataSource, data];

        publishDps({
          general_timing: encodeAllNormalTimer(newList as NormalTimerData[]),
        });
      }
    }
    if (key === 'cycleTiming') {
      if (isEdit) {
        const newList = cycleDataSource.map(item => {
          if (item.id == data.id) {
            return data;
          }
          return item;
        });
        publishDps({
          cycle_timing1: encodeAllCycleTimer(newList as CycleTimerData[]).circle1,
          cycle_timing2: encodeAllCycleTimer(newList as CycleTimerData[]).circle2,
        });
      } else {
        const newList = [...cycleDataSource, data];
        publishDps({
          cycle_timing1: encodeAllCycleTimer(newList as CycleTimerData[]).circle1,
          cycle_timing2: encodeAllCycleTimer(newList as CycleTimerData[]).circle2,
        });
      }
    }

    callback(false, 'success');
  } else {
    callback(true, res.conflictTip);
  }
};

export const toLoopDecimal = loopStr => {
  const loop = loopStr.split('').reduce((acc, cur, idx) => {
    return acc + (cur === '1' ? Math.pow(2, idx) : 0);
  }, 0);
  return loop;
};

/**
 * 将`普通定时数组`转换为`普通定时16进制字符串`
 * @param {Array} datas - 普通定时数组,
 */
export const encodeNormalTimer = (data: NormalTimerData, isRaw = true) => {
  if (!data) return '';
  const { power, loops, startTime, waterTime, id, waterArea } = data;

  const idPorwestr = (Number(id).toString(2) + (power ? '1' : '0')).padStart(8, '0');

  const hexIdPowerStr = parseInt(idPorwestr, 2).toString(16).toUpperCase().padStart(2, '0');

  const startTimes = numToHexString(startTime, 4);
  const waterTimes = numToHexString(waterTime, 4);
  const waterAreaStr = AreaArr[waterArea];
  const loopStr = toLoopDecimal(loops.join(''));

  const loop = numToHexString(loopStr);

  const hexString = `${hexIdPowerStr}${startTimes}${waterAreaStr}${waterTimes}${
    loop === '00' ? '80' : loop
  }`;
  // string型定时需要转base64
  return `${hexString}`;
};

export const formatTimeString = (time: number) => {
  const hours = Math.floor(time / 60);
  const minutes = time % 60;

  if (hours === 0) {
    return `${minutes.toString().padStart(2, '0')} ${Strings.getLang('min')}`;
  }
  return `${hours.toString().padStart(2, '0')} ${Strings.getLang('hour')} ${minutes
    .toString()
    .padStart(2, '0')} ${Strings.getLang('min')}`;
};

export const encodeCircleTimer = (data: CycleTimerData, isRaw = true) => {
  if (!data) return '';
  const { power, loops, startTime, endTime, waterTime, pauseTime, id, waterArea } = data;

  const idPorwestr = (Number(id).toString(2) + (power ? '1' : '0')).padStart(8, '0');

  const hexIdPowerStr = parseInt(idPorwestr, 2).toString(16).toUpperCase().padStart(2, '0');

  const startTimes = numToHexString(startTime, 4);

  const endTimes = numToHexString(endTime, 4);

  const waterTimes = numToHexString(waterTime, 4);

  const pauseTimes = numToHexString(pauseTime, 4);

  const waterAreaStr = AreaArr[waterArea];

  const loopStr = toLoopDecimal(loops.join(''));

  const loop = numToHexString(loopStr);

  const hexString = `${hexIdPowerStr}${startTimes}${endTimes}${waterTimes}${pauseTimes}${waterAreaStr}${
    loop === '00' ? '80' : loop
  }`;
  // string型定时需要转base64
  return `${hexString}`;
};

export const encodeAllNormalTimer = (datas: NormalTimerData[]) => {
  if (!datas?.length) return '00';
  return datas.map(item => encodeNormalTimer(item, false)).join('');
};

export const encodeAllCycleTimer = (datas: CycleTimerData[]) => {
  const circle1 = datas.filter(item => +item.id <= 21);
  const circle2 = datas.filter(item => +item.id > 21);
  return {
    circle1: circle1.map(item => encodeCircleTimer(item, false)).join('') || '00',
    circle2: circle2.map(item => encodeCircleTimer(item, false)).join('') || '00',
  };
};

// 下次灌溉时间
export const getNearlyTask = (
  timerList: (CycleTimerData | NormalTimerData)[],
  setDelayTime: number,
  weatherDelayTime: string,
  is12Hour = false
) => {
  console.log(`🚀 ~ getNearlyTask timerList:`, timerList);
  if (!timerList.length) {
    return {
      nextTime: '--',
      waterTime: '--',
      nextTimeValue: 0,
    };
  }

  const nowDay = dayjs().day();
  const nowDate = dayjs().date();
  const nowHour = dayjs().hour();
  const nowMinute = dayjs().minute();
  const nowStartMinute = dayjs().startOf('day').unix();

  const minuteTime = nowHour * 60 + nowMinute;
  // 用当前时间比较定时任务中最接近的时间
  const futureTimes: any = [];
  const dayMinutes = 86400;

  let delayEndTime: number | undefined = undefined;

  if (endsWith(weatherDelayTime, 'h')) {
    const weatherDelayRealTime = +weatherDelayTime.slice(0, 2);
    delayEndTime = dayjs(setDelayTime).add(weatherDelayRealTime, 'hours').unix();
  }

  map(timerList, (item: CycleTimerData | NormalTimerData) => {
    const { startTime, endTime } = item;
    const loops = item.loops.join('');
    const nextTime = nowStartMinute + startTime * 60;
    const nowMinuteTime = nowStartMinute + (nowHour * 60 + nowMinute) * 60;
    console.log(`🚀 ~ startTime:`, startTime, minuteTime);

    if (loops === '0000000') {
      let currentTime = nextTime;
      if (startTime >= minuteTime) {
        futureTimes.push({ value: currentTime, item });
      }
      if (startTime <= minuteTime) {
        console.log('123123', '123123');
        currentTime += dayMinutes;
        futureTimes.push({ value: currentTime, item });
      }

      if (minuteTime > startTime && minuteTime < endTime && item.type === 'cycleTime') {
        currentTime = nextTime;
        const cycleItem: CycleTimerData = item as CycleTimerData;
        const oneLoopTime = cycleItem.waterTime + cycleItem.pauseTime;

        while (currentTime < nowMinuteTime) {
          currentTime += oneLoopTime * 60;
        }

        if (currentTime < endTime * 60 + nowStartMinute) {
          futureTimes.push({ value: currentTime, item });
        }
      }
    } else {
      map(loops, (day, index: number) => {
        if (day === '0') return;
        let addDay = 0;
        if (index === nowDay) {
          addDay = startTime > minuteTime ? 0 : 7;
        } else if (index > nowDay) {
          addDay = index - nowDay;
        } else {
          addDay = 7 - (nowDay - index);
        }

        if (
          index === nowDay &&
          startTime < minuteTime &&
          minuteTime < endTime &&
          item.type === 'cycle'
        ) {
          const cycleItem: CycleTimerData = item as CycleTimerData;
          const oneLoopTime = cycleItem.waterTime + cycleItem.pauseTime;
          let currentTime = nextTime + addDay * dayMinutes;
          while (currentTime < nowMinuteTime) {
            currentTime += oneLoopTime * 60;
          }
          if (oneLoopTime < endTime) {
            futureTimes.push({ value: currentTime, item });
          }
        }
        // 周循环只要最后的时候再过滤一遍就ok了
        futureTimes.push({ value: nextTime + addDay * dayMinutes, item });
        futureTimes.push({ value: nextTime + (addDay + 7) * dayMinutes, item });
      });
    }
  });

  let minData = { value: Infinity };
  // 这里的判断仅作为过滤
  if (delayEndTime) {
    if (delayEndTime && setDelayTime) {
      forEach(futureTimes, data => {
        const { value } = data;
        if (value > Number(delayEndTime) && value < minData.value) {
          minData = data;
        }
      });
    }
  } else {
    forEach(futureTimes, data => {
      const { value } = data;
      if (value < minData.value) {
        minData = data;
      }
    });
  }

  if (get(minData, 'item')) {
    console.log("🚀 ~ getNearlyTask ~ get(minData, 'item':", get(minData, 'item'));

    const title = {
      nextTime: dayjs(minData.value * 1000).format('YYYY/MM/DD HH:mm'),
      nextTimeValue: minData.value * 1000,
      waterTime: formatTimeString(+minData?.item?.waterTime),
    };

    return title;
  }
  return {
    nextTime: '--',
    waterTime: '--',
    nextTimeValue: 0,
  };
};
